/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package driver;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import util.SimplePoolExecutor;
import web.ConfigEnv;
import web.WebQuery;
import web.WebQueryInstance;
import web.WebSite;
import xml.XMLHelper;


/**
 *
 * @author keith
 */
public class GoogleGroupWebExtract {

  final private Logger myLogger = AppLog.appLog;
  final private SimplePoolExecutor pool = new SimplePoolExecutor();
  final private List<Runnable> pending = new ArrayList<Runnable>();

  private boolean running = false;
  private ConfigEnv configEnv;
  private File baseDir;


  /** Creates a new instance of GoogleGroupWebExtract
   * @param configEnv
   * @param baseDir 
   * @throws FileNotFoundException
   * @throws SAXException
   * @throws MalformedURLException
   * @throws IOException 
   */
  public GoogleGroupWebExtract( final ConfigEnv configEnv, File baseDir ) throws
    FileNotFoundException, SAXException, MalformedURLException, IOException {

    this.configEnv = configEnv;
    this.baseDir = baseDir;

//    cache = new CacheManager( configEnv );
  }


  /**
   * Augment the contents of the site configuration DOM with contents of the 
   * referenced "credentials-properties".
   * 
   * @param doc the site configuration DOM
   */
  private void updateCredentials( Document doc ) {
    final XPathFactory factory = XPathFactory.newInstance();
    final XPath xpath = factory.newXPath();
    try {
      final Element credentials =
        (Element) xpath.evaluate( "//credentials[credentials-properties]", doc,
                                  XPathConstants.NODE );
      if ( credentials != null ) {
        NodeList credPropNodeList = credentials.getElementsByTagName(
          "credentials-properties" );
        final String credPropFilename =
          credPropNodeList.item( 0 ).getTextContent();
        final File credPropFile = new File( configEnv.getConfigDir(),
                                            credPropFilename );

        Properties credProp = new Properties();
        try {

          credProp.load( new FileReader( credPropFile ) );

          for ( Entry entry : credProp.entrySet() ) {
            Element key = doc.createElement( entry.getKey().toString() );
            credentials.appendChild( key );
            key.appendChild( doc.createTextNode( entry.getValue().toString() ) );
          }
        } catch ( FileNotFoundException ex ) {
          myLogger.log( Level.SEVERE, "Can't read property file: " + credPropFilename, ex );

        } catch ( IOException ex ) {
          myLogger.log( Level.SEVERE, null, ex );
        }
      }
    } catch ( XPathExpressionException ex ) {
      myLogger.log(  Level.SEVERE, null, ex );
    }
  }


  private WebQuery makeWebQuery( WebSite site, Element dom ) {
    final Document uGen = XMLHelper.extractDOMDocument( dom, "url-generator" );
    final Document xGen = XMLHelper.extractDOMDocument( dom, "xml-generator" );
    final String fileName = XMLHelper.getNodeValue( dom, "file");
    File file = null;
    
    if ( fileName.length() > 0 ){
      file = new File( baseDir, fileName );
    }
    
    return new WebQuery( site, uGen, xGen, file );
  }


  public void driver() {
    for ( File query : configEnv.getWebQueries() ) {
      myLogger.log( Level.FINE, query.getAbsolutePath() );

      try {
        Document doc = XMLHelper.parse( query );

        updateCredentials( doc );
//        XMLHelper.dump( doc );
        WebSite site = new WebSite( pool, doc.getDocumentElement() );

        queueTask( site );

        // Obtain any authentication that is required...
//        webSiteList.add(site);

        NodeList pageNodeList = doc.getElementsByTagName( "page" );

        for ( int idx = 0, len = pageNodeList.getLength(); idx < len; idx++ ) {
          Element page = (Element) pageNodeList.item( idx );
          WebQuery wq = makeWebQuery( site, page );

          site.execute( new WebQueryInstance( null, wq, 1 ) );
//break;
//          webQueryList.add(wq);
//System.out.println( wq );
        }
        establishThreadPoolSize();
      } catch ( FileNotFoundException e ) {
        e.printStackTrace();
      } catch ( SAXException e ) {
        e.printStackTrace();
      } catch ( IOException e ) {
        e.printStackTrace();
      }
//break;
    }

    myLogger.log( Level.INFO, "Awaiting completion..." );
    pool.awaitExecution();

    pool.shutdownNow();
  }


  private void establishThreadPoolSize() {
//    throw new UnsupportedOperationException("Not yet implemented");
  }


  /**
   * 
   * @param r 
   */
  private void queueTask( WebSite r ) {
    synchronized ( pending ) {
      if ( running ) {
        pool.execute( r );
      } else {
        pending.add( r );
      }
    }
  }


  /**
   * 
   */
  public void start() {
    synchronized ( pending ) {
      running = true;

      for ( Runnable r : pending ) {
        pool.execute( r );
      }
      pending.clear();
    }
  }


  public static void main( String[] args ) throws FileNotFoundException,
                                                  IOException, SAXException {
    final Logger myLogger = AppLog.appLog;
    final ConfigEnv configEnv = ConfigEnv.getConfiguration( "config" );
    
    String baseDirName = null;
    
System.out.println( "Default encoding: " + Charset.defaultCharset().displayName());
    if ( args.length > 0 ){
      baseDirName = args[ 0 ];
    } else {
      baseDirName = System.getProperty( "user.dir");
    }
    
    final File baseDir = new File( baseDirName );

    if ( ! configEnv.isValid() ) {
      myLogger.log( Level.SEVERE, "Invalid configuration file." );
    } else if ( ! baseDir.exists() ){
      myLogger.log( Level.SEVERE, "baseDir: \"" + baseDirName + "\" does not exist." );
    } else {
      final GoogleGroupWebExtract process =
        new GoogleGroupWebExtract( configEnv, baseDir );

      process.start();

      process.driver();
    }

    myLogger.log( Level.INFO, "Done." );
  }
}
